package lex

import (
	"unsafe"

	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
)

// Scan 一行扫描器
// 分析一行的词法单元
type Scan struct {
	s string
	ScanConfig
	lbrack       int //left square bracket
	rbrack       int //Right bracket
	frontBracket bool
}

type ScanConfig struct {
	Errctx                *errcode.ErrCtx
	IsMLC                 *bool
	File                  string
	Line                  int
	EnableSinLineComments bool //是否启用单行注释
}

// NewScan 创建Scan
//   - s要进行分析的一行字符串，必须已经去除换行符
//   - cf是一些非必须的参数
func NewScan(s string, cf ScanConfig) Scan {
	ret := Scan{s: s}
	ret.ScanConfig = cf
	return ret
}

// Reset 重新设置扫描的行和字符串
func (s *Scan) Reset(str string, Line int) {
	s.s = str
	s.Line = Line
	s.lbrack = 0
	s.rbrack = 0
	s.frontBracket = false
}

// AllScan 返回所有的词法单元
func (s *Scan) AllScan() []Token {
	slen := len(s.s)
	if slen == 0 {
		return nil
	}
	var ret = make([]Token, 0, 5)
	left := 0
	i := 0
	for ; i < slen; i++ {
		switch s.s[i] {
		case ' ', '\t': //空格或制表符
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			i++
			if i >= slen { //扫描完毕退出
				return ret
			}
		skip:
			for index := i; index < slen; index++ { //连续的分隔符跳过
				switch s.s[index] {
				case ' ', '\t': //空格或制表符
					continue skip
				default: //不是空格或制表符
					i = index
					break skip
				}
			}
			left = i //记录第一个非分隔符位置
			i--      //因为当前s.s[i]不是空格，循环尾i++跳到下一字符，所以i--
		case '+':
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			if i == slen-1 {
				addToken(&ret, i, &left, ADD, "+")
				continue
			}
			if s.s[i+1] == '+' { //如果是自增
				i++
				addToken(&ret, i, &left, Inc, "++") //记录自增
				continue
			}
			addToken(&ret, i, &left, ADD, "+") //记录加号
		case '-':
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			if i == slen-1 {
				addToken(&ret, i, &left, SUB, "-")
				continue
			}
			if s.s[i+1] == '-' { //如果是自减
				i++
				addToken(&ret, i, &left, Dec, "--") //记录自减
				continue
			}
			if isNum(s.s[i+1]) && len(ret) != 0 && possible_negNum(ret[len(ret)-1].TYPE) { //如果后面是数字,并且前面是赋值或逗号或左小括号或相等或不等或if或小于或大于
				right := i + 2
				decimal_point := 0
				for index := i + 2; index < slen; index++ { //寻找小数后整数的结尾
					if !isNum(s.s[index]) {
						if s.s[index] == '.' {
							decimal_point++
							if decimal_point != 1 {
								break
							}
							continue
						}
						right = index
						break
					}
				}
				if decimal_point == 1 { //如果是有小数点
					i = right - 1
					addToken(&ret, i, &left, FLOAT, s.s[left:right])
					continue
				} else if decimal_point == 0 { //如果没有小数点
					i = right - 1
					addToken(&ret, i, &left, NUMBER, s.s[left:right])
					continue
				}
			}
			addToken(&ret, i, &left, SUB, "-") //记录减号
		case '(':
			autoAddToken(s, &ret, left, i)        //记录之前的一个词法单元
			addToken(&ret, i, &left, LPAREN, "(") //记录左小括号
		case ')':
			autoAddToken(s, &ret, left, i)        //记录之前的一个词法单元
			addToken(&ret, i, &left, RPAREN, ")") //记录右小括号
		case ',':
			autoAddToken(s, &ret, left, i)       //记录之前的一个词法单元
			addToken(&ret, i, &left, Comma, ",") //记录逗号
		case ':':
			autoAddToken(s, &ret, left, i)       //记录之前的一个词法单元
			addToken(&ret, i, &left, Colon, ":") //记录冒号
		case '=':
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			if i == slen-1 {
				addToken(&ret, i, &left, ASSIGN, "=")
				continue
			}
			if s.s[i+1] == '=' { //如果是比较相等
				i++
				addToken(&ret, i, &left, Equal, "==") //记录比较相等
				continue
			}
			addToken(&ret, i, &left, ASSIGN, "=") //记录赋值
		case '}':
			autoAddToken(s, &ret, left, i)        //记录之前的一个词法单元
			addToken(&ret, i, &left, RBRACE, "}") //记录右大括号
		case '{':
			autoAddToken(s, &ret, left, i)        //记录之前的一个词法单元
			addToken(&ret, i, &left, LBRACE, "{") //记录左大括号
		case '/':
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			if i == slen-1 {               //是除号
				addToken(&ret, i, &left, DIV, "/")
				continue
			}
			if s.s[i+1] == '/' { //如果后面是注释
				if s.EnableSinLineComments { //如果启用单行注释
					addToken(&ret, i, &left, SinLineComments, s.s[i:])
				}
				return ret
			}
			if s.s[i+1] == '*' { //如果是多行注释开头
				addToken(&ret, i, &left, MLCStart, "/*")
				*s.IsMLC = true
				return ret
			}
			addToken(&ret, i, &left, DIV, "/") //记录除号
		case '*':
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			if i == slen-1 {               //是乘号
				addToken(&ret, i, &left, MUL, "*")
				continue
			}
			if s.s[i+1] == '/' { //如果是多行注释结束
				addToken(&ret, i, &left, MLCEnd, "*/")
				return ret
			}
			addToken(&ret, i, &left, MUL, "*") //记录乘号
		case '!':
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			if i == slen-1 {               //如果只有!,当前识别为符号
				addToken(&ret, i, &left, NAME, "!")
				return ret
			}
			if s.s[i+1] == '=' { //如果是 !=
				i++
				addToken(&ret, i, &left, NoEqual, "!=") //按当前语言规范记录为不等号
				continue
			}
			addToken(&ret, i, &left, NAME, "!") //按当前语言规范记录为符号
		case '>':
			autoAddToken(s, &ret, left, i)         //记录之前的一个词法单元
			addToken(&ret, i, &left, Greater, ">") //按当前语言规范记录为大于号
		case '<':
			autoAddToken(s, &ret, left, i)      //记录之前的一个词法单元
			addToken(&ret, i, &left, Less, "<") //按当前语言规范记录为小于号
		case ';':
			autoAddToken(s, &ret, left, i)           //记录之前的一个词法单元
			addToken(&ret, i, &left, SEMICOLON, ";") //按当前语言规范记录为分号
		case '%':
			autoAddToken(s, &ret, left, i)        //记录之前的一个词法单元
			addToken(&ret, i, &left, Remain, "%") //按当前语言规范记录为取余数
		case '^':
			autoAddToken(s, &ret, left, i)     //记录之前的一个词法单元
			addToken(&ret, i, &left, Xor, "^") //按当前语言规范记录为异或
		case '|':
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			if i == slen-1 {               //是位或
				addToken(&ret, i, &left, Or, "|")
				return ret
			}
			if s.s[i+1] == '|' { //如果是 ||
				i++
				addToken(&ret, i, &left, LogicOr, "||") //按当前语言规范记录为逻辑或
				continue
			}
			addToken(&ret, i, &left, Or, "|") //按当前语言规范记录为位或
		case '&':
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			if i == slen-1 {               //是&
				addToken(&ret, i, &left, OnlyLEA, "&")
				return ret
			}
			if s.s[i+1] == '&' { //如果是 &
				i++
				addToken(&ret, i, &left, LogicAND, "&&") //按当前语言规范记录为逻辑与
				continue
			}
		case '"': //是字符串开头
			autoAddToken(s, &ret, left, i) //记录之前的一个词法单元
			s.parserString(&ret, &i, &left, s.s)
		case '[': //是左中括号
			if left != i { //如果之前有一个词法单元,记录之前的一个词法单元
				ret = append(ret, s.parserToken(s.s[left:i]))
				ok := true
				for len(ret) >= 2 && ok {
					ok = isJoin(ret[len(ret)-2].TYPE, ret[len(ret)-1].TYPE)
					if len(ret) >= 2 && ok {
						ret[len(ret)-2] = NewMoreThanJustTokensWithBrackts(ret[len(ret)-2], ret[len(ret)-1])
						ret = ret[0 : len(ret)-1]
					}
				}
			}
			s.lbrack++
			ok := false
			var tk Token
			if left != i {
				tk = s.parserToken(s.s[left:i])
				left = i
			}
		skip2:
			for index := i + 1; index < slen; index++ {
				switch s.s[index] {
				case '[': //是左中括号
					s.lbrack++
					continue skip2
				case ']': //是右中括号
					s.rbrack++
					if s.lbrack == s.rbrack {
						s.lbrack, s.rbrack, ok, i = 0, 0, true, index
						if tk.TYPE == NAME || tk.TYPE == LEA { //如果前面是符号或取地址
							ret[len(ret)-1] = NewMoreThanJustTokensWithBrackts(ret[len(ret)-1], NewToken(TokenWithBrackets, s.s[left:index+1]))
						} else if len(ret) > 0 && ret[len(ret)-1].TYPE == MoreThanJustTokensWithBrackts && s.frontBracket { //如果前面是不止中括号的Token
							ret = append(ret[:len(ret)-1], NewMoreThanJustTokensWithBrackts(ret[len(ret)-1], NewToken(TokenWithBrackets, s.s[left:index+1])))
						} else {
							addToken(&ret, index, &left, TokenWithBrackets, s.s[left:index+1])
						}
						s.frontBracket, left = true, i+1
						break skip2
					}
					continue skip2
				}
			}
			if !ok { //如果左右中括号数量不匹配
				s.ScanConfig.Errctx.Panic(s.File, s.Line, errcode.NewMsgBracketsNoEqual(s.lbrack, s.rbrack), errcode.TheNumOfLeftRightBracketsNoMatch)
				return ret
			}
		}
	}
	autoAddToken(s, &ret, left, i)
	return ret
}

func (scan *Scan) parserToken(s string) Token {
	switch s {
	case "var":
		return NewToken(VAR, "var")
	case enum.Func:
		return NewToken(FUNC, enum.Func)
	case "if":
		return NewToken(IF, "if")
	case "else":
		return NewToken(ELSE, "else")
	case "for":
		return NewToken(FOR, "for")
	case "break":
		return NewToken(Break, "break")
	case "continue":
		return NewToken(Continue, "continue")
	case "struct":
		return NewToken(Struct, "struct")
	case "interface":
		return NewToken(Interface, "interface")
	case "return":
		return NewToken(Return, "return")
	case "goto":
		return NewToken(GOTO, "goto")
	case enum.Const:
		return NewToken(Const, enum.Const)
	case enum.Nil:
		return NewToken(Nil, enum.Nil)
	case "package":
		return NewToken(Package, "package")
	case enum.Switch:
		return NewToken(Switch, enum.Switch)
	case enum.Case:
		return NewToken(Case, enum.Case)
	case enum.Default:
		return NewToken(Default, enum.Default)
	case enum.Method:
		return NewToken(Method, enum.Method)
	case enum.AutoFree:
		return NewToken(AutoFree, enum.AutoFree)
	case "and":
		return NewToken(AND, "and")
	case "import":
		return NewToken(Import, "import")
	case enum.Enum:
		return NewToken(Enum, enum.Enum)
	default:
		bol, ret := checkNumber(s)
		if bol { //如果是数字
			return NewToken(ret, s)
		}
		bol, ret = checkBool(s)
		if bol { //如果是true或false
			return NewToken(ret, s)
		}
		bol, ret, str := checkLea(s)
		if bol { //如果是取地址
			return NewToken(ret, str)
		}
		bol, ret, str = checkDeref(s)
		if bol { //如果是解引用
			return NewToken(ret, str)
		}
		if isNum(s[0]) { //如果符号第一个字符是数字，报错
			scan.Errctx.Panic(scan.File, scan.Line, errcode.NewMsgSymbol(s), errcode.OneNoNumber)
		}
		return NewToken(NAME, s) //如果其他规则不能成功分析出单词标记，分析为符号
	}
}

func addToken(ret *[]Token, i int, left *int, typ TokenType, str string) {
	(*ret) = append((*ret), NewToken(typ, str))
	*left = i + 1 //记录第一个非分隔符位置
}

func autoAddToken(s *Scan, ret *[]Token, left, i int) {
	s.frontBracket = false
	if left != i { //如果之前有一个词法单元
		(*ret) = append((*ret), s.parserToken(s.s[left:i]))
		ok := true
		for len(*ret) >= 2 && ok {
			ok = isJoin((*ret)[len(*ret)-2].TYPE, (*ret)[len(*ret)-1].TYPE)
			if len(*ret) >= 2 && ok {
				(*ret)[len(*ret)-2] = NewMoreThanJustTokensWithBrackts((*ret)[len(*ret)-2], (*ret)[len(*ret)-1])
				(*ret) = (*ret)[0 : len(*ret)-1]
			}
		}
	}
}

func isJoin(a, b TokenType) bool {
	if a == MoreThanJustTokensWithBrackts && b == TokenWithBrackets {
		//这是为了合并形如 &s[1] 或 &s[1][1]的索引表达式
		return true
	}
	//这是为了合并形如 [1][1]s 或 [1][1]&s 或 [1]&s 或 [1]s 的数组类型
	return a == TokenWithBrackets && (b == MoreThanJustTokensWithBrackts || b == NAME || b == LEA)
}

// checkBool 判断是不是true或false
func checkBool(s string) (bool, TokenType) {
	switch s {
	case "true": //是true
		return true, TRUE
	case "false": //是false
		return true, FALSE
	}
	return false, 0 //都不是
}

// checkLea 判断是不是取地址
func checkLea(s string) (bool, TokenType, string) {
	if s[0] == '&' {
		if len(s) == 1 { //只有取地址
			return true, OnlyLEA, "&"
		}
		// 返回s整个值而不返回s[1:]是因为如果不这样，那么类型T和指针&T的属性值将是一样的
		return true, LEA, s //是如同&a的取地址
	}
	return false, 0, "" //不是
}

// parserString 从左到右解析出 如同"文本"的字符串
func (s *Scan) parserString(ret *[]Token, i, left *int, str string) {
	old := *i
	for *i < len(str)-1 {
		*i++                //因为发现"i没加，所以循环开始加
		if str[*i] == '"' { //发现"
			if str[*i-1] != '\\' { //发现不是\",也就是如同"文本"
				(*ret) = append((*ret), NewToken(String, str[old:*i+1]))
				*left = *i + 1
				return
			}
		}
	}
	s.Errctx.Panic(s.File, s.Line, nil, errcode.StringNoEnd)
}

// scanMLCEnd 返回是否有符合规范（前面只能有空格或制表符）的多行注释结束
func scanMLCEnd(s string) bool {
	slen := len(s)
	for i := 0; i < slen; i++ { //从左到右扫描字符串
		switch s[i] {
		case ' ', '\t': //空格或制表符
			i++
			if i >= slen { //扫描完毕退出
				return false
			}
		skip:
			for index := i; index < slen; index++ { //连续的分隔符跳过
				switch s[index] {
				case ' ', '\t': //空格或制表符
					continue skip
				default: //不是空格或制表符
					i = index
					break skip
				}
			}
			i-- //因为当前s.s[i]不是空格，循环尾i++跳到下一字符，所以i--
		case '*':
			if i+1 <= slen-1 && s[i+1] == '/' { //如果是多行注释结束
				return true
			}
		default: //如果在多行注释结束前有不是空格或制表符的字符
			return false
		}
	}
	return false
}

// checkDeref判断是不是解引用
func checkDeref(s string) (bool, TokenType, string) {
	if s[0] == '@' {
		if len(s) == 1 { //只有解引用
			return true, OnlyDeref, "@"
		}
		return true, Deref, s //是如同@a的解引用
	}
	return false, 0, "" //不是
}

// possible negative number 可能的负数
func possible_negNum(typ TokenType) bool {
	return typ == ASSIGN || typ == Comma || typ == LPAREN || typ == Equal || typ == NoEqual || typ == IF || typ == Less || typ == Greater
}

// NewMoreThanJustTokensWithBrackts 创建不止有中括号的Token
func NewMoreThanJustTokensWithBrackts(tks ...Token) Token {
	ret := Token{TYPE: MoreThanJustTokensWithBrackts}
	var retvalue []Token = tks
	p := (*MoreThanJustTokensWithBracktsS)(unsafe.Pointer(&ret.Value))
	p.Ptr = &retvalue
	return ret
}

// MoreThanJustTokensWithBracktsS 不止有中括号的Token结构体
type MoreThanJustTokensWithBracktsS struct {
	Ptr *[]Token
}

func init() {
	if unsafe.Sizeof(MoreThanJustTokensWithBracktsS{}) > unsafe.Sizeof("") {
		panic("unsafe.Sizeof(MoreThanJustTokensWithBracktsS) > unsafe.Sizeof(\"\")")
	}
}
